package com.atguigu.order.orders.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.enumtion.OrderEnum;
import com.atguigu.common.to.UmsMemberEntity;
import com.atguigu.common.to.es.SkuHasStock;
import com.atguigu.common.to.rabbit.OrderToWaresTo;
import com.atguigu.common.utils.R;
import com.atguigu.order.orders.contant.OrderContant;
import com.atguigu.order.orders.entity.OmsOrderItemEntity;
import com.atguigu.order.orders.exception.WareLockException;
import com.atguigu.order.orders.feign.CartFeign;
import com.atguigu.order.orders.feign.MemberFeign;
import com.atguigu.order.orders.feign.ProductFeign;
import com.atguigu.order.orders.feign.WareFeign;
import com.atguigu.order.orders.interceptor.OrderInterceptor;
import com.atguigu.order.orders.service.OmsOrderItemService;
import com.atguigu.order.vo.*;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.common.base.Joiner;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.order.orders.dao.OmsOrderDao;
import com.atguigu.order.orders.entity.OmsOrderEntity;
import com.atguigu.order.orders.service.OmsOrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;



//@RabbitListener(queues = "order.release.order.queue")
@Service("omsOrderService")
public class OmsOrderServiceImpl extends ServiceImpl<OmsOrderDao, OmsOrderEntity> implements OmsOrderService {


    @Autowired
    private MemberFeign memberFeign;

    @Autowired
    private CartFeign cartFeign;

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WareFeign wareFeign;

    @Autowired
    private ProductFeign productFeign;

    @Autowired
    private OmsOrderItemService orderItemService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OmsOrderEntity> page = this.page(
                new Query<OmsOrderEntity>().getPage(params),
                new QueryWrapper<OmsOrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 结算页面需要展示的信息
     *
     * @return
     */
    public OrderConfirmVo toTrade() throws ExecutionException, InterruptedException {
        /**
         * 由于两个业务都是互不干扰的--可以将其使用异步编排来做
         */

        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        //拿到上下文对象
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        //获取登录用户id
        Long id = OrderInterceptor.local.get().getId();
        System.out.println(Thread.currentThread().getId());
        CompletableFuture<Void> addressFuther = CompletableFuture.runAsync(() -> {
            //将上下文对象赋值给这个线程
            RequestContextHolder.setRequestAttributes(requestAttributes);
            System.out.println(Thread.currentThread().getId());
            //远程调用会员服务查看地址信息
            List<MemberAddressVo> addressVos = memberFeign.orderToMember(id);
            orderConfirmVo.setAddress(addressVos);
        }, executor);


        //远程调用接口查看对应选中的购物车数据
        CompletableFuture<Void> cartFuther = CompletableFuture.runAsync(() -> {
            //将上下文对象赋值给这个线程
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> itemVos = cartFeign.orderToCart();
            orderConfirmVo.setItems(itemVos);
        }, executor).thenRunAsync(() -> {
            //接着远程调用库存服务看查询对应的商品是否存在货物
            List<Long> ids = orderConfirmVo.getItems().stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R r = wareFeign.skuIdInfo(ids);
            List<SkuHasStock> data = r.getData(new TypeReference<List<SkuHasStock>>() {
            });
            Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuHasStock::getSkuId, (m) -> {
                if (m.getHasStock() > 0) {
                    return true;
                }
                return false;
            }));
            orderConfirmVo.setStocks(map);

        }, executor);

        //阻塞等待--执行完成之后
        CompletableFuture.allOf(addressFuther, cartFuther).get();
        //在生成界面响应的时候生成一个uuid,相当于一个令牌，用来解决幂等性问题--有效期为30分钟
        String str = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(OrderContant.N0_DEBEAT_TOKEN_PRE + id, str, 30, TimeUnit.MINUTES);
        orderConfirmVo.setOrderToken(str);
        return orderConfirmVo;
    }

    /**
     * 提交订单的service
     *
     * @param submitVo
     * @return private Long addrId;//收货地址的id
     * private Integer payType;//支付方式
     * //无需提交需要购买的商品，去购物车再获取一遍
     * //优惠，发票
     * private String orderToken;//防重令牌
     * private BigDecimal payPrice;//应付价格  验价
     * private String note;//订单备注
     */
    @Transactional
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo submitVo) {
        //定义返回对象
        SubmitOrderResponseVo submitOrderResponseVo = new SubmitOrderResponseVo();
        //TODO 验证令牌防止用户点击多次提交订单--需要保证原子性操作
        //获取用户
        UmsMemberEntity umsMemberEntity = OrderInterceptor.local.get();
        //lua脚本说明获取数据对比成功返回1并且删除，失败返回0
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long result = (Long) redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(OrderContant.N0_DEBEAT_TOKEN_PRE + umsMemberEntity.getId()), submitVo.getOrderToken());
        if (result != 1) {
            //说明删除失败--返回原先界面--重新生成token-失败可能是过期或者是重复点击（包括执行成功后按返回键再次执行)
            submitOrderResponseVo.setCode(1);
            return submitOrderResponseVo;
        }
        //1.将信息保存到订单表中

        //1.1新增数据到订单项表中--从购物车中拿到数据即可
        List<OrderItemVo> orderItemVos = cartFeign.orderToCart();
        List<OmsOrderItemEntity> orderItems = orderItemVos.stream().map((m) -> {
            OmsOrderItemEntity omsOrderItemEntity = new OmsOrderItemEntity();
            //定义sku信息
            omsOrderItemEntity.setSkuId(m.getSkuId());
            omsOrderItemEntity.setSkuName(m.getTitle());
            omsOrderItemEntity.setSkuPic(m.getImage());
//            omsOrderItemEntity.setSkuPrice(m.getPrice());
            omsOrderItemEntity.setSkuQuantity(m.getCount());
            omsOrderItemEntity.setSkuAttrsVals(Joiner.on(",").join(m.getSkuAttr()));
            //定义spu信息
            /*
                    `spu_id` bigint(20) DEFAULT NULL COMMENT 'spu_id',
                  `spu_name` varchar(255) DEFAULT NULL COMMENT 'spu_name',
                  `spu_pic` varchar(500) DEFAULT NULL COMMENT 'spu_pic',
                  `spu_brand` varchar(200) DEFAULT NULL COMMENT '品牌',
                    `category_id` bigint(20) DEFAULT NULL COMMENT '商品分类id',
             */

            //todo 远程调用商品服务获取spu相关信息
            R r = productFeign.orderSpu(m.getSkuId());

            OrderToProductQuerySpuInfoVo orderToProductQuerySpuInfoVo = r.getData("order", new TypeReference<OrderToProductQuerySpuInfoVo>() {
            });
            //将返回来的spu信息拷贝到订单项信息中
            BeanUtils.copyProperties(orderToProductQuerySpuInfoVo, omsOrderItemEntity);
            //设置优惠信息
            omsOrderItemEntity.setPromotionAmount(new BigDecimal(0.0));
            omsOrderItemEntity.setCouponAmount(new BigDecimal(0.0));
            omsOrderItemEntity.setIntegrationAmount(new BigDecimal(0.0));
            omsOrderItemEntity.setRealAmount(new BigDecimal(0.0));
            omsOrderItemEntity.setGiftGrowth(0);
            omsOrderItemEntity.setGiftIntegration(0);
            return omsOrderItemEntity;
        }).collect(Collectors.toList());


        //1.2.新增数据到订单表中
        OmsOrderEntity entity = new OmsOrderEntity();

        //生成订单号并设置
        entity.setOrderSn(IdWorker.getTimeId());
        //设置会员id
        entity.setMemberId(umsMemberEntity.getId());
        entity.setCreateTime(new Date());
        entity.setMemberUsername(umsMemberEntity.getUserName());
        //设置订单总额
        BigDecimal bigDecimal = new BigDecimal(0.0);
        //设置商品促销分解金额
        BigDecimal promotions = new BigDecimal(0.0);
        //设置优惠券优惠分解金额
        BigDecimal coupons = new BigDecimal(0.0);
        //设置积分优惠分解金额
        BigDecimal integrations = new BigDecimal(0.0);
        //设置该商品经过优惠后的分解金额
        BigDecimal reals = new BigDecimal(0.0);
        //设置总积分
        Integer integration = 0;
        //设置成长值
        Integer growth = 0;
        for (OmsOrderItemEntity orderItem : orderItems) {
            BigDecimal multiply = orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuQuantity()));
            bigDecimal = bigDecimal.add(multiply);
            promotions = promotions.add(orderItem.getPromotionAmount());
            coupons = coupons.add(orderItem.getCouponAmount());
            integrations = integrations.add(orderItem.getIntegrationAmount());
            reals = reals.add(orderItem.getRealAmount());
            integration = integration + orderItem.getGiftIntegration();
            growth = growth + orderItem.getGiftGrowth();
        }
        entity.setTotalAmount(bigDecimal);
        //设置应付总额-总价-优惠总价+运费（忽略）
        entity.setPayAmount(bigDecimal.subtract(promotions.add(coupons.add(integrations).add(reals))));
        //设置优惠总额
        entity.setCouponAmount(coupons);
        entity.setIntegrationAmount(integrations);
        entity.setPromotionAmount(promotions);
        entity.setPayType(1);//支付宝沙箱支付
        entity.setSourceType(0); //默认都是pc下单
        entity.setStatus(0);  //刚创建的订单为待付款状态
        //设置获得的总积分和总成长值
        entity.setGrowth(growth);
        entity.setIntegration(integration);
        /*
          发票内容不设置
         */
        //设置收货人信息--远程调用查询
        UmsMemberReceiveAddressEntity addressEntity = memberFeign.orderToMemberById(submitVo.getAddrId());
        entity.setReceiverName(addressEntity.getName());
        entity.setReceiverCity(addressEntity.getCity());
        entity.setReceiverDetailAddress(addressEntity.getDetailAddress());
        entity.setReceiverPhone(addressEntity.getPhone());
        entity.setReceiverPostCode(addressEntity.getPostCode());
        entity.setReceiverProvince(addressEntity.getProvince());
        entity.setReceiverRegion(addressEntity.getRegion());
        entity.setNote(submitVo.getNote());


        //保存订单
        this.save(entity);
        //发消息给mq
        rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",entity);
        //设置订单项信息
        List<OmsOrderItemEntity> omsOrderItemEntities = orderItems.stream().map((m) -> {
            m.setOrderId(entity.getId());
            return m;
        }).collect(Collectors.toList());
        //批量提交保存
        orderItemService.saveBatch(omsOrderItemEntities);


        //2.远程调用库存服务将对应的库存锁住
        List<OrderToWareTo> orderToWareTos = orderItems.stream().map((m) -> {
            OrderToWareTo orderToWareTo = new OrderToWareTo();
            orderToWareTo.setOrderId(entity.getId());
            orderToWareTo.setOrderSn(entity.getOrderSn());
            orderToWareTo.setSkuId(m.getSkuId());
            orderToWareTo.setNum(m.getSkuQuantity());
            return orderToWareTo;
        }).collect(Collectors.toList());
        R r = wareFeign.orderLock(orderToWareTos);
        Boolean lock = r.getData(new TypeReference<Boolean>() {
        });
        if(!lock){
            //表示没锁住--需要回滚
            submitOrderResponseVo.setCode(1);
            throw new WareLockException("库存不足，没有锁住");


        }

        submitOrderResponseVo.setCode(0);
        submitOrderResponseVo.setOrder(entity);
        //3.远程调用积分服务完成积分的扣减

        return submitOrderResponseVo;
    }


    /**
     * 监听队列中的消息
     */
    public void listenOrder(OmsOrderEntity orderEntity, Channel channel, Message message) throws IOException {
        System.out.println("用户没有付款需要自动解除订单");
        //一段时间后拿到这个订单--首先需要判断是否存在这个订单
        OmsOrderEntity omsOrderEntity = this.getById(orderEntity.getId());
        //拿到最新的订单数据--然后判断状态
        if(omsOrderEntity!=null){
            if(omsOrderEntity.getStatus()== OrderEnum.NO_PAY.getCode()){
                //判断订单状态是否为待付款--需要解除订单-解锁库存
                omsOrderEntity.setStatus(OrderEnum.CLOSED.getCode());
                //更新订单状态
                this.updateById(omsOrderEntity);
//                channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                OrderToWaresTo orderToWaresTo=new OrderToWaresTo();
                orderToWaresTo.setOrderId(omsOrderEntity.getId());
                orderToWaresTo.setOrderSn(omsOrderEntity.getOrderSn());
                //发消息给库存--自动解锁库存
                rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderToWaresTo);

            }else {
//                channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                //用户把这个订单删除了--需要解锁库存
                //说明订单出现问题--回滚了--不去处理该条消息--需要解锁库存
            }
        }else {
//            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }

    }



}